#include "Dictionary.h"

#include <fstream>
#include <iostream>
#include <map>
#include <vector>
#include <string>
using namespace std;

Dictionary::Dictionary() {
    this->theActiveNumber = static_cast<std::size_t>(-1);
	this->word="";
}

//a dictionary can be constructed with words from a file
Dictionary::Dictionary(string aFileName) {
    this->theActiveNumber = static_cast<std::size_t>(-1);
    this->readIn( aFileName );
	this->word="";
}

//for now, only the name of the file is needed
//Probably, it should be changed so that a ifstream is the parameter
bool Dictionary::readIn(string aFileName) {
    bool success = false;
    ifstream file;
    file >> noskipws;
    file.open( aFileName.c_str() );
    if(file.is_open() == true) {
        string temp;
        while(file.eof() == false) {
            getline(file, temp);
            this->theWords[ temp.length() ].push_back( temp );
        }
        success = true;
    }
    file.close();
    file.clear();
    return success;
}

bool Dictionary::isActive() {
    return ( this->theActiveNumber == static_cast<std::size_t>(-1) );
}

bool Dictionary::setActive(size_t aNumber) {
	if( aNumber < this->theWords.size() && this->theWords[aNumber].size() != 0 ) {
        this->deActivate();
        this->theActiveNumber = aNumber;
        list<string>::iterator aListIterator = this->theActiveList.begin();
        this->theActiveList.splice( aListIterator, this->theWords[ this->theActiveNumber ] );
		for(size_t x=0;x<aNumber;x++){
			this->word+=" ";
		}
        return true;

    } else {
        return false;
    }
}

bool Dictionary::removeAll(char aCharacter) {
    //if this is active, do something
    if( this->theActiveNumber != static_cast<std::size_t>(-1) ) {
        list<string> tempList;
        list<string>::iterator start = this->theActiveList.begin();
        list<string>::iterator stop = this->theActiveList.end();
        //go through the list
        while( start != stop ) {
            //if the node's string has the character, remove it
            if( start->find( aCharacter ) != string::npos ) {
                //iterators used for splicing becomes useless, so a temp iterator is needed
                list<string>::iterator tempIterator = start;
                list<string>::iterator aListIterator = tempList.end();
                //increment this iterator in case it becomes useless afterwards
                ++start;
                tempList.splice( aListIterator, this->theActiveList, tempIterator );
            } else {
                ++start;
            }
        }
        //if every word in the list has the character
        //      put everything back for now
        //else put those words back to where they should be
        if( this->theActiveList.empty() == true) {

			//Have a map of a vecor of the positions to the number of the times that pattern happened.
			map<vector<int>, int> ranks;

			//Find all the possible patterns.
			for(list<string>::iterator it=tempList.begin();it!=tempList.end();++it){
				vector<int> location;
				for(size_t y=0;y<it->size();y++){
					if((*it)[y]==aCharacter){
						location.push_back(y);
						//cout<<"\t\t"<<y;
					}
				}
				//cout<<endl;
				//have a count as to how many of the pattern has been seen.
				if(location.size()!=0){
					if(ranks.find(location)!=ranks.end()){
						ranks[location]=ranks[location]+1;
					}else{
						ranks.insert(make_pair(location,1));
					}
				}
			}

			//if there is are patterns find the most common one.
			if(ranks.size()!=0){
				int maxPattern=0;
				vector<int> pattern;
				for(map<vector<int>, int>::iterator it=ranks.begin();it!=ranks.end();++it){
					if(it->second>maxPattern){
						maxPattern=it->second;
						pattern=it->first;
					}
				}
				//TODO::::::do the looking at all patterns first then compare with the case that it is a bigger list than than non-lettered words
				
				//if a word is in the pattern add it.
				if(maxPattern!=0){
					for(list<string>::iterator it=tempList.begin();it!=tempList.end();++it){
						bool inPattern=true;
						std::size_t totalChar=0;
						for(size_t x=0;x<it->size();x++){
							if((*it)[x]==aCharacter){
								if(!inThePatten(x,pattern)){
									inPattern=false;
								}
								else{
									++totalChar;
								}
							}
						}
						if( inPattern==true && totalChar==pattern.size() ){
							//cout<<"look here: "<<(*it)<<endl;
							this->theActiveList.insert(theActiveList.begin(),*it);
						}
					}
				}else{
					this->theActiveList.splice( stop, tempList );
					return false;
				}

				//Update the word.
				for(size_t x=0;x<pattern.size();x++){
					word[pattern[x]]=aCharacter;
				}
				//cout<<"\t|"<<word<<endl;
				return true;
			}else{
				this->theActiveList.splice( stop, tempList );
				return false;
			}

        } else {

            list<string>::iterator aListIterator = this->theWords[ this->theActiveNumber ].end();
            this->theWords[ this->theActiveNumber ].splice( aListIterator, tempList );

            return true;
        }
    } else {
        return false;
    }
}


bool Dictionary::removeAll2(char aCharacter) {
    //if this is active, do something
    if( this->theActiveNumber != static_cast<std::size_t>(-1) ) {
        list<string> tempList;
        list<string>::iterator start = this->theActiveList.begin();
        list<string>::iterator stop = this->theActiveList.end();
        //map< vector<int>,set<string> > list;
        list<pair<vector<int>, list<string> > >listing;
        //cout<<"1"<<endl;
        //go through the list
        while( start != stop ) {
            vector<int> pos;
            //cout<<"1.1"<<endl;
            for(size_t x=0;x<start->size();++x){
                if( (*start)[x]==aCharacter){
                    pos.push_back(x);
                }
            }
            //cout<<"1.2"<<endl;
            bool found = false;
            list<pair<vector<int>, list<string> > >::iterator it;
            for(it=listing.begin();it!=listing.end()&&!found;++it){
                if(it->first==pos){
                    found=true;
					it->second.push_front(*start);
                }
            }
            if(!found){
                list<string> s;
				s.push_front(*start);
				listing.push_front(make_pair(pos,s));
            }            
            ++start;
        }
        //cout<<"2"<<endl;
        //Find the biggest set.
        list<pair<vector<int>, list<string> > >::iterator it;
        list<string> max=listing.begin()->second;
        vector<int> maxPos=listing.begin()->first;
        //cout<<"3"<<endl;
        for(it=listing.begin();it!=listing.end();++it){
            if(it->second.size()>max.size()){
                max=it->second;
                maxPos=it->first;
            }
        }

        //cout<<"4"<<endl;
        for(size_t x=0;x<maxPos.size();x++){
            this->word[maxPos[x]]=aCharacter;
        }
        //cout<<"5"<<endl;
        this->theActiveList.clear();
        list<string>::iterator it2;
        for(it2=max.begin();it2!=max.end();++it2){
            theActiveList.push_back((*it2));
        }
        //cout<<"6"<<endl;
    }
       return true; 
}

bool Dictionary::removeAll3(char aCharacter) {
    if( this->theActiveNumber != static_cast<std::size_t>(-1) ) {
        size_t max = 0;
        map<vector<int>, list<string>> aMap;
        while(this->theActiveList.size() > 0) {
            vector<int> pos;
            string *aWord = &(this->theActiveList.front());
            for(size_t i = 0; i < aWord->size(); ++i) {
                if(aWord->at(i) == aCharacter) {
                    pos.push_back(i);
                }
            }
            list<string> *ptr = &aMap[pos];
            ptr->push_back(*aWord);
            if(ptr->size() > max) {
                max = ptr->size();
            }
            this->theActiveList.pop_front();
        }
        map<vector<int>, list<string>>::iterator i = aMap.begin();
        list<string> *ptr;
        while(i != aMap.end()) {
            if(i->second.size() < max) {
                ptr = &(this->theWords[ this->theActiveNumber ]);
            } else {
                ptr = &(this->theActiveList);
                ++max;
            }
            ptr->splice(ptr->end(), i->second);
            ++i;
        }
    }
    return false;
}

bool Dictionary::removeAll4(char aCharacter) {
    //if this is active, do something
    if( this->theActiveNumber != static_cast<std::size_t>(-1) ) {
        size_t max = 0;
        map<double, list<string>> aMap;
        while(this->theActiveList.size() > 0) {
            double pos = 1.0;
            string *aWord = &(this->theActiveList.front());
            for(size_t i = 0; i < aWord->size(); ++i) {
                pos *= 10;
                if(aWord->at(i) == aCharacter) {
                    ++pos;
                }
            }
            list<string> *ptr = &aMap[pos];
            ptr->push_back(*aWord);
            if(ptr->size() > max) {
                max = ptr->size();
            }
            this->theActiveList.pop_front();
        }
        map<double, list<string>>::iterator i = aMap.begin();
        list<string> *ptr;
        while(i != aMap.end()) {
            if(i->second.size() < max) {
                ptr = &(this->theWords[ this->theActiveNumber ]);
            } else {
                ptr = &(this->theActiveList);
                ++max;
            }
            ptr->splice(ptr->end(), i->second);
            ++i;
        }
    }
    return false;
}

void Dictionary::deActivate() {
    if( this->isActive() == false ) {
        list<string>::iterator aListIterator = this->theWords[ this->theActiveNumber ].end();
        this->theWords[ this->theActiveNumber ].splice( aListIterator, this->theActiveList );
        this->theActiveNumber = static_cast<std::size_t>(-1);
		this->word="";
    }
}

const list<string>& Dictionary::getActiveList() {
    return this->theActiveList;
}

string Dictionary::getWord(){
	return this->word;
}

bool Dictionary::inThePatten(int at,vector<int> pattern){
	bool result=false;
	for(size_t x=0;x<pattern.size();++x){
		if(pattern[x]==at){
			result = true;
		}
	}
	return result;

}

